﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Xml.Linq;
using SharePointUtilitiesForDevelopers.Gateway;
using SharePointUtilitiesForDevelopers.Mapper;

namespace SharePointUtilitiesForDevelopers.Repository
{
    /// <summary>
    /// A repository for working with lists.
    /// </summary>
    public class SharePointListRepository : ISharePointListRepository
    {
        #region Fields

        private readonly ISharePointListsServiceGateway _listsServiceGateway;
        private readonly ISharePointViewsServiceGateway _viewsServiceGateway;
        private readonly IMapper<XElement, SharePointListItem> _listItemMapper;
        private readonly IMapper<XElement, SharePointContentType> _contentTypeMapper;
        private readonly IMapper<XElement, SharePointList> _listMapper;
        private readonly IMapper<XElement, SharePointField> _fieldMapper;

        #endregion Fields

        #region Constructors
        
        /// <summary>
        /// Initializes a new instance of the <see cref="SharePointListRepository"/> class.
        /// </summary>
        /// <param name="listsServiceGateway">The lists service gateway.</param>
        /// <param name="viewsServiceGateway">The views service gateway.</param>
        /// <param name="listItemMapper">The list item mapper.</param>
        /// <param name="contentTypeMapper">The content type mapper.</param>
        /// <param name="listMapper">The list mapper.</param>
        /// <param name="fieldMapper">The field mapper.</param>
        public SharePointListRepository(
            ISharePointListsServiceGateway listsServiceGateway, 
            ISharePointViewsServiceGateway viewsServiceGateway,
            IMapper<XElement, SharePointListItem> listItemMapper,
            IMapper<XElement, SharePointContentType> contentTypeMapper,
            IMapper<XElement, SharePointList> listMapper,
            IMapper<XElement, SharePointField> fieldMapper)
        {
            _listsServiceGateway = listsServiceGateway;
            _viewsServiceGateway = viewsServiceGateway;
            _listItemMapper = listItemMapper;
            _contentTypeMapper = contentTypeMapper;
            _listMapper = listMapper;
            _fieldMapper = fieldMapper;

        }

        #endregion Constructors
        
        #region Methods
        
        /// <summary>
        /// Deletes list items in the specified list / view.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="viewName">Name of the view.</param>
        /// <param name="batchSize">Size of the batch.</param>
        /// <param name="queryXml">The query Xml.</param>
        public void DeleteListItems(
            string siteUrl,
            string listName,
            string viewName,
            string queryXml, 
            int batchSize)
        {
            // Delete the list items
            _listsServiceGateway.DeleteListItems(
                siteUrl, 
                listName, 
                viewName,
                SharePointXml.ParseXml(queryXml, "Query"),
                batchSize);
        }

        /// <summary>
        /// Gets the list items.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="viewName">Name of the view.</param>
        /// <param name="queryXml">The query Xml.</param>
        /// <param name="viewFieldsXml">The viewfields Xml.</param>
        /// <param name="rowLimit">The row limit.</param>
        /// <param name="queryOptionsXml">The query options Xml.</param>
        /// <param name="webId">The web id.</param>
        /// <returns>
        /// A list <see cref="SharePointListItem"/> objects.
        /// </returns>
        public IEnumerable<SharePointListItem> GetListItems(
            string siteUrl, 
            string listName, 
            string viewName, 
            string queryXml, 
            string viewFieldsXml, 
            int? rowLimit, 
            string queryOptionsXml, 
            string webId)
        {
            // Get the list items
            var listItems = _listsServiceGateway.GetListItems(
                siteUrl, 
                listName, 
                viewName, 
                SharePointXml.ParseXml(queryXml, "Query"),
                SharePointXml.ParseXml(viewFieldsXml, "ViewFields"), 
                rowLimit.ToString(),
                SharePointXml.ParseXml(queryOptionsXml, "QueryOptions"),
                webId,
                _listItemMapper);

            return listItems;

        }
        
        /// <summary>
        /// Gets the lists in the specified web.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <returns>A list of <see cref="SharePointList"/> objects.</returns>
        public IEnumerable<SharePointList> GetLists(string siteUrl)
        {
            // Get the lists
            var lists = _listsServiceGateway.GetLists(siteUrl, _listMapper);
            return lists;
        }

        /// <summary>
        /// Gets the list views.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <returns>A list of views.</returns>
        public IEnumerable<SharePointListView> GetListViews(string siteUrl, string listName)
        {
            // Get the views
            var views = _viewsServiceGateway.GetListViews(siteUrl, listName).ToList();
            
            var viewsWithDetail = new List<SharePointListView>();

            // Get detail about each view
            foreach(var view in views)
            {
                // Get detail about the view
                var viewWithDetails = GetListView(siteUrl, listName, view.Name);
                viewsWithDetail.Add(viewWithDetails);
            }

            return viewsWithDetail;
        }

        /// <summary>
        /// Gets the list view.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="viewName">Name of the view.</param>
        /// <returns>A list view./returns>
        public SharePointListView GetListView(string siteUrl, string listName, string viewName)
        {
            // Get the view
            var view = _viewsServiceGateway.GetListView(siteUrl, listName, viewName);
            return view;
        }

        /// <summary>
        /// Gets the list content types.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <returns>The list content types</returns>
        public IEnumerable<SharePointContentType> GetListContentTypes(string siteUrl, string listName)
        {
            // Get the content types
            var contentTypes = _listsServiceGateway.GetListContentTypes(siteUrl, listName, _contentTypeMapper);

            var contentTypesWithDetail = new List<SharePointContentType>();

            // Get detail about each content type
            foreach (var contentType in contentTypes)
            {
                // Get detail about content type
                var contentTypeWithDetail = GetListContentType(siteUrl, listName, contentType.Id);
                contentTypesWithDetail.Add(contentTypeWithDetail);
            }

            return contentTypesWithDetail;
        }

        /// <summary>
        /// Gets the list content type.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="contentTypeId">The content type id.</param>
        /// <returns>The list content type.</returns>
        public SharePointContentType GetListContentType(string siteUrl, string listName, string contentTypeId)
        {
            // Get the content type
            var contentType = _listsServiceGateway.GetListContentType(siteUrl, listName, contentTypeId, _contentTypeMapper);

            // Remove fields that are part of the list and not truly part of the content type
            contentType.Fields = contentType.Fields.Where(field => string.Compare(field.SourceId, listName,
                                                          StringComparison.InvariantCultureIgnoreCase) != 0).ToList().AsReadOnly();

            return contentType;
        }

        /// <summary>
        /// Gets the list fields.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <returns>The list fields.</returns>
        public IEnumerable<SharePointField> GetListFields(string siteUrl, string listName)
        {
            // Get the fields
            var fields = _listsServiceGateway.GetListFields(siteUrl, listName, _fieldMapper);

            return fields;
        }

        /// <summary>
        /// Gets the list custom fields.
        /// </summary>
        /// <param name="fields">The fields.</param>
        /// <param name="listName">Name of the list.</param>
        /// <returns>The list of custom fields.</returns>
        public IEnumerable<SharePointField> GetListCustomFields(IEnumerable<SharePointField> fields, string listName)
        {
            // Find the custom fields
            return fields.Where(field => string.Compare(field.SourceId, listName,
                                                          StringComparison.InvariantCultureIgnoreCase) == 0).ToList();
        }

        /// <summary>
        /// Gets the list.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <param name="listName">Name of the list.</param>
        /// <param name="options">The options.</param>
        public SharePointList GetList(string siteUrl, string listName, SharePointListLoadOptions options)
        {
            // Get the list
            var list = _listsServiceGateway.GetList(siteUrl, listName, _listMapper);

            // Load fields
            if(options.Fields)
            {
                LoadListFields(list, siteUrl);
            }

            // Load content types
            if(options.ContentTypes)
            {
                LoadListContentTypes(list, siteUrl);
            }

            // Load views
            if(options.Views)
            {
                LoadListViews(list, siteUrl);
            }

            return list;
        }

        /// <summary>
        /// Loads the list fields.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="siteUrl">The site URL.</param>
        public void LoadListFields(SharePointList list, string siteUrl)
        {
            list.Fields = GetListFields(siteUrl, list.Name).ToList().AsReadOnly();
            list.CustomFields = GetListCustomFields(list.Fields.AsEnumerable(), list.Name).ToList().AsReadOnly();
        }

        /// <summary>
        /// Loads the list content types.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="siteUrl">The site URL.</param>
        public void LoadListContentTypes(SharePointList list, string siteUrl)
        {
            list.ContentTypes = GetListContentTypes(siteUrl, list.Name).ToList().AsReadOnly();
        }

        /// <summary>
        /// Loads the list views.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="siteUrl">The site URL.</param>
        public void LoadListViews(SharePointList list, string siteUrl)
        {
            list.Views = GetListViews(siteUrl, list.Name).ToList().AsReadOnly();
            list.DefaultView = (from view in list.Views where view.DefaultView select view).FirstOrDefault();
        }

        /// <summary>
        /// Gets the web site user list.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <returns>The site user list for the specified site.</returns>
        public SharePointList GetWebSiteUserInfoList(string siteUrl)
        {
            return _listsServiceGateway.GetList(siteUrl, SharePointBuiltInListId.UserInfo, _listMapper);
        }

        /// <summary>
        /// Gets the web lists.
        /// </summary>
        /// <param name="siteUrl">The site URL.</param>
        /// <returns>A list of lists in the web.</returns>
        public IEnumerable<SharePointList> GetWebLists(string siteUrl)
        {
            return _listsServiceGateway.GetLists(siteUrl, _listMapper);
        }

        #endregion Methods
    }
}